package me.flyray.bsin.infrastructure.biz.paychannel.wxpay;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.github.binarywang.wxpay.bean.request.BaseWxPayRequest;
import com.github.binarywang.wxpay.service.WxPayService;
import lombok.extern.slf4j.Slf4j;
import me.flyray.bsin.domain.entity.PayChannelConfig;
import me.flyray.bsin.infrastructure.biz.paychannel.AbstractPaymentService;
import me.flyray.bsin.infrastructure.biz.paychannel.PayChannelConfigBiz;
import me.flyray.bsin.payment.channel.wxpay.model.*;
import me.flyray.bsin.payment.common.AbstractRes;
import me.flyray.bsin.payment.enums.PayChannelInterfaceEnum;
import me.flyray.bsin.payment.enums.PayMerchantModeEnum;
import me.flyray.bsin.payment.utils.PayWayUtil;
import me.flyray.bsin.payment.channel.wxpay.WxRefundRes;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/*
 * 支付接口： 微信官方
 * 支付方式： 自适应
 * @author leonard
 * @date 2025/07/24 14:05
 */
@Component
@Slf4j
public class WxPayPaymentService extends AbstractPaymentService {

  @Autowired protected PayChannelConfigBiz payChannelConfigBiz;

  @Override
  public String getPayChannelCode() {
    return PayChannelInterfaceEnum.WXPAY.getCode();
  }

  @Override
  public AbstractRes pay(UnifyOrderReq request) throws Exception {
    return PayWayUtil.getRealPaywayV3Service(this, request.getPayWay()).pay(request);
  }

  @Override
  public AbstractRes refund(RefundUnifyReq request) throws Exception {
    // 根据支付方式调用对应的退款处理
    return PayWayUtil.getRealPaywayV3Service(this, request.getPayWay()).refund(request);
  }

  /**
   * 构建微信APIV3接口 统一下单请求数据
   *
   * @param request 统一支付请求对象
   * @param notifyUrl 回调地址
   * @param wxPayService 微信支付服务
   * @param payChannelConfig 支付渠道配置
   * @return WxPayV3OrderRequestModel
   * @author terrfly
   */
  public WxPayV3OrderRequestModel buildV3OrderRequestModel(
      UnifyOrderReq request,
      String notifyUrl,
      WxPayService wxPayService,
      PayChannelConfig payChannelConfig) {
    // 微信统一下单请求对象
    WxPayV3OrderRequestModel result = new WxPayV3OrderRequestModel();
    result.setOutTradeNo(request.getOutOrderNo());
    result.setDescription(request.getSubject());

    // 订单失效时间，遵循rfc3339标准格式，格式为yyyy-MM-DDTHH:mm:ss+TIMEZONE,示例值：2018-06-08T10:34:56+08:00
    result.setTimeExpire(
        String.format(
            "%sT%s+08:00",
            DateUtil.format(request.getExpiredTimeDate(), DatePattern.NORM_DATE_FORMAT),
            DateUtil.format(request.getExpiredTimeDate(), DatePattern.NORM_TIME_FORMAT)));

    // 回调地址 - 支持带参数的微信支付回调
    String notifyUrlStr = getPayNotifyUrl(notifyUrl, request.getOutOrderNo());
    log.info("微信支付回调地址: {}\n\n\n\n", notifyUrlStr);
    result.setNotifyUrl(notifyUrlStr);

    // 订单金额
    result.setAmount(
        new WxPayV3OrderRequestModel.Amount()
            .setCurrency("CNY")
            .setTotal(BaseWxPayRequest.yuan2Fen(request.getPayAmount())));

    if (StringUtils.isNotBlank(request.getClientIp())) {
      result.setSceneInfo(
          new WxPayV3OrderRequestModel.SceneInfo().setPayerClientIp(request.getClientIp()));
    } else {
      log.info("商户IP为空，请检查商户信息");
    }
    // 订单分账， 将冻结商户资金。
    if (isProfitSharingOrder(request)) {
      log.info("订单分账： 设置 用户分账服务标识， 将冻结商户资金。");
      result.setSettleInfo(new WxPayV3OrderRequestModel.SettleInfo().setProfitSharing(true));
    }

    // 特约商户
    if (PayMerchantModeEnum.SPECIAL_MERCHANT_MODE.getCode().equals(request.getMerchantPayMode())) {
      log.info("商户模式为特约商户模式");
      WxPayIsvParams isvParams =
          (WxPayIsvParams)
              payChannelConfigBiz.queryIsvParams(request.getPayChannel(), payChannelConfig);

      WxPayIsvSubMchParams isvSubMchParams =
          (WxPayIsvSubMchParams)
              payChannelConfigBiz.queryIsvSubMchParams(request.getPayChannel(), payChannelConfig);
      // 服务商相关参数
      result.setIsvAppid(isvParams.getAppId());
      result.setIsvMchid(isvParams.getMchId());
      // 子商户相关参数
      result.setSubMchid(wxPayService.getConfig().getSubMchId());
      result.setSubMchid(isvSubMchParams.getSubMchId());
      if (StringUtils.isNotBlank(isvSubMchParams.getSubMchAppId())) {
        result.setSubAppid(isvSubMchParams.getSubMchAppId());
      }
      // 子商户subAppId不为空
      if (StringUtils.isNotBlank(result.getSubAppid())) {
        result.setPayer(
            new WxPayV3OrderRequestModel.Payer()
                .setSubOpenid(request.getChannelUserId())); // 用户在子商户appid下的唯一标识

      } else {
        result.setPayer(
            new WxPayV3OrderRequestModel.Payer().setSpOpenid(request.getChannelUserId()));
      }
    }
    // 普通商户
    else {
      log.info("商户模式为普通商户模式");
      // openId
      result.setPayer(
          new WxPayV3OrderRequestModel.Payer().setNormalOpenId(request.getChannelUserId()));
      WxPayNormalMchParams normalMchParams =
          (WxPayNormalMchParams)
              payChannelConfigBiz.queryNormalMchParams(request.getPayChannel(), payChannelConfig);
      result.setNormalMchid(normalMchParams.getMchId());
      result.setNormalAppid(normalMchParams.getAppId());
    }
    channelMchParamsBuild(request, result);

    // 清理空对象，避免发送无效参数
    result.cleanEmptyObjects();

    return result;
  }

  /**
   * 构建微信APIV3接口 退款请求数据
   *
   * @param request 统一退款请求对象
   * @param notifyUrl 回调地址
   * @return JSONObject
   * @author leonard
   */
  public JSONObject buildWxRefundRequestV3(RefundUnifyReq request, String notifyUrl) {
    JSONObject refundRequest = new JSONObject();

    // 原商户订单号
    refundRequest.put("out_trade_no", request.getOutTradeNo());

    // 微信订单号 - 如果有的话优先使用
    if (StringUtils.isNotBlank(request.getTransactionId())) {
      refundRequest.put("transaction_id", request.getTransactionId());
    }

    // 商户退款单号
    refundRequest.put("out_refund_no", request.getOutRefundNo());

    // 退款原因
    refundRequest.put("reason", request.getRefundReason());

    // 退款金额信息
    JSONObject amount = new JSONObject();
    // 退款金额，单位：分（元转分）
    amount.put(
        "refund", request.getRefundAmount().multiply(new java.math.BigDecimal("100")).intValue());
    // 原订单金额，单位：分
    amount.put(
        "total", request.getTotalAmount().multiply(new java.math.BigDecimal("100")).intValue());
    // 币种
    amount.put("currency", request.getCurrency());
    refundRequest.put("amount", amount);

    // 退款结果回调url
    if (StringUtils.isNotBlank(request.getNotifyUrl())) {
      refundRequest.put("notify_url", request.getNotifyUrl());
    } else if (StringUtils.isNotBlank(notifyUrl)) {
      refundRequest.put("notify_url", getRefundNotifyUrl(notifyUrl, request.getOutRefundNo()));
    }

    log.info(
        "构建微信退款V3请求完成，outTradeNo: {}, outRefundNo: {}, refundAmount: {}",
        refundRequest.getString("out_trade_no"),
        refundRequest.getString("out_refund_no"),
        amount.getInteger("refund"));

    return refundRequest;
  }

  /**
   * 将微信退款结果映射到响应对象
   *
   * @param wxRefundResult 微信退款结果
   * @param response 响应对象
   * @author leonard
   */
  public void mapWxRefundResultToResponse(JSONObject wxRefundResult, WxRefundRes response) {
    response.setRefundId(wxRefundResult.getString("refund_id"));
    response.setOutRefundNo(wxRefundResult.getString("out_refund_no"));
    response.setTransactionId(wxRefundResult.getString("transaction_id"));
    response.setOutTradeNo(wxRefundResult.getString("out_trade_no"));
    response.setChannel(wxRefundResult.getString("channel"));
    response.setUserReceivedAccount(wxRefundResult.getString("user_received_account"));
    response.setSuccessTime(wxRefundResult.getString("success_time"));
    response.setCreateTime(wxRefundResult.getString("create_time"));
    response.setStatus(wxRefundResult.getString("status"));
    response.setFundsAccount(wxRefundResult.getString("funds_account"));

    // 处理金额信息
    JSONObject amountJson = wxRefundResult.getJSONObject("amount");
    if (amountJson != null) {
      WxRefundRes.Amount amount = new WxRefundRes.Amount();
      amount.setTotal(amountJson.getInteger("total"));
      amount.setRefund(amountJson.getInteger("refund"));
      amount.setPayerTotal(amountJson.getInteger("payer_total"));
      amount.setPayerRefund(amountJson.getInteger("payer_refund"));
      amount.setSettlementRefund(amountJson.getInteger("settlement_refund"));
      amount.setSettlementTotal(amountJson.getInteger("settlement_total"));
      amount.setDiscountRefund(amountJson.getInteger("discount_refund"));
      amount.setCurrency(amountJson.getString("currency"));
      response.setAmount(amount);
    }

    // 处理优惠退款信息
    response.setPromotionDetail(wxRefundResult.getString("promotion_detail"));
  }

  private void channelMchParamsBuild(UnifyOrderReq request, WxPayV3OrderRequestModel result) {
    MchParams wxpayMchParams = new MchParams();
    wxpayMchParams.setAppid(
        PayMerchantModeEnum.SPECIAL_MERCHANT_MODE.getCode().equals(request.getMerchantPayMode())
            ? result.getIsvAppid()
            : result.getNormalAppid());
    wxpayMchParams.setMchid(
        PayMerchantModeEnum.SPECIAL_MERCHANT_MODE.getCode().equals(request.getMerchantPayMode())
            ? result.getIsvMchid()
            : result.getNormalMchid());
    wxpayMchParams.setSubAppid(
        PayMerchantModeEnum.SPECIAL_MERCHANT_MODE.getCode().equals(request.getMerchantPayMode())
            ? result.getSubAppid()
            : null);
    wxpayMchParams.setSubMchid(
        PayMerchantModeEnum.SPECIAL_MERCHANT_MODE.getCode().equals(request.getMerchantPayMode())
            ? result.getSubMchid()
            : null);
    request.setChannelMchParams(JSON.toJSONString(wxpayMchParams));
  }
}
